package encryption

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"net/url"
)

// Golang 的AES 加密是根据密钥的长度来选择 128 位（16） 192（24） 以及 256（32）

const (
	ENCODING_BASE64 = "BASE64"
	ENCODING_HEX    = "HEX"

	ENCRYPT_MODE_ECB = "ECB"
	ENCRYPT_MODE_CBC = "CBC"

	BYTELENGTH = 256
)

type AESEncrypter struct {
	Key          string // 加密Key
	Iv           string // 偏移量
	EncodingMode string // 编码模式
	EncryptMode  string // 加密模式
	UrlEncoded   bool   // 是否使用URLEncode 来继续编码已经完成的结果
}

func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func PKCS7UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

func (c *AESEncrypter) Encrypt(str string) (result string, err error) {
	aesBlockEncrypter, err := aes.NewCipher([]byte(c.Key))
	content := PKCS7Padding([]byte(str), aesBlockEncrypter.BlockSize())
	encrypted := make([]byte, len(content))
	if err != nil {
		println(err.Error())
		return "", err
	}
	aesEncrypter := cipher.NewCBCEncrypter(aesBlockEncrypter, []byte(c.Iv))
	aesEncrypter.CryptBlocks(encrypted, content)
	if c.EncodingMode == ENCODING_HEX {
		return fmt.Sprintf("%x", encrypted), nil
	}
	base64Result := base64.StdEncoding.EncodeToString(encrypted)
	if c.UrlEncoded {
		result = url.QueryEscape(base64Result)
	} else {
		result = base64Result
	}
	return result, nil
}

func (c *AESEncrypter) decrypt(str string) (result string, err error) {
	var bytesToDecrypt []byte
	if c.EncodingMode == ENCODING_HEX {
		bytesToDecrypt, err = hex.DecodeString(str)
		if err != nil {
			return "", err
		}
	} else {
		var baseStr string
		if c.UrlEncoded {
			baseStr, err = url.QueryUnescape(str)
			if err != nil {
				return "", err
			}
		} else {
			baseStr = str
		}
		bytesToDecrypt, err = base64.StdEncoding.DecodeString(baseStr)
		if err != nil {
			return "", err
		}
	}
	//创建实例
	block, err := aes.NewCipher([]byte(c.Key))
	if err != nil {
		return "", err
	}
	//获取块的大小
	blockSize := block.BlockSize()
	//使用cbc
	blockMode := cipher.NewCBCDecrypter(block, []byte(c.Iv)[:blockSize])
	//初始化解密数据接收切片
	crypted := make([]byte, len(bytesToDecrypt))
	//执行解密
	blockMode.CryptBlocks(crypted, bytesToDecrypt)
	//去除填充
	crypted = PKCS7UnPadding(crypted)
	return string(crypted), nil
}
